#ifndef __JK_STRING_H_
#define __JK_STRING_H_

#include "jk_base.h"
#include <assert.h>

JK_LIB_NAMESPACE_BEGIN


template <typename T>
class StringT
{
public:
    StringT()
        : buffer_(NULL)
    {
        Initialize();
    }

    StringT(T* str)
        : buffer_(NULL)
    {
        assert(str != NULL);
        Initialize();
        (*this) = str;
    }

    StringT(const StringT<T>& str)
    {
        (*this) = str;
    }

    ~StringT()
    {
        Destory();
    }

public:
    void Clear()
    {
        if (buffer_ != NULL)
            buffer_[0] = zero;
    }

    int GetLength() const
    {
        return length_of_str_;
    }

    bool IsEmpty() const
    {
        return (length_of_str_ == 0);
    }

public:
    StringT<T> operator+(const T* str) const
    {
        StringT<T> temp = (*this);
        temp += str;
        return (temp);
    }

    StringT<T> operator+(const StringT<T>& str) const
    {
        StringT<T> temp = (*this);
        temp += str;
        return (temp);
    }

    StringT<T>& operator+=(const T* str)
    {
        int length = GetLengthOf(str);
        if (length + length_of_str_ >= capacity_)
            Expand(length_of_str_ + length + 1);

        StringCopy(buffer_ + length_of_str_, capacity_ - length_of_str_, str, length + 1);
        length_of_str_ += length;

        return (*this);
    }

    StringT<T>& operator+=(const StringT<T>& str)
    {
        if (length_of_str_ + str.length_of_str_ >= capacity_)
            Expand(length_of_str_ + str.length_of_str_ + 1);

        StringCopy(buffer_ + length_of_str_, capacity_ - length_of_str_, str.buffer_, str.length_of_str_);

        buffer_[length_of_str_--] = zero;
        return (*this);
    }

    StringT<T>& operator=(const StringT<T>& str)
    {
        if (this == &str)
            return (str);

        if (capacity_ < str.size_of_buffer_)
            Expand(str.size_of_buffer_);

        StringCopy(buffer_, capacity_, str.buffer_, length_of_str_);

        buffer_[length_of_str_] = zero;
        length_of_str_ = str.length_of_str_;
        return (*this);
    }

    StringT<T>& operator=(const T* str)
    {
        assert(str != NULL);
        int length = GetLengthOf(str);
        if (length >= capacity_)
            Expand(length + 1);
        
        length_of_str_ = 0;
        StringCopy(buffer_, capacity_, str, length);

        buffer_[length_of_str_] = zero;
        return (*this);
    }

    T& operator[](int nIndex) const
    {
        return GetElementAt(nIndex);
    }

public:
    static int GetLengthOf(const T* str)
    {
        int length = -1;
        while (str[++length] != zero);
        return length;
    }

    static void StringCopy(T* str_dest, const int dest_length, const T* str_src, const int copy_length)
    {
        memcpy_s(str_dest, dest_length * sizeof(T), str_src, copy_length * sizeof(T));
    }

private:
    void Expand(int size_need)
    {
        if (size_need <= capacity_)
            return;

        do 
        {
            capacity_ += 32;
        } while (capacity_ < size_need);

        T* temp = new T[capacity_];
        for (int i = 0; i < length_of_str_; i++)
            temp[i] = buffer_[i];

        if (buffer_ != NULL)
        {
            delete []buffer_;
            buffer_ = NULL;
        }
        buffer_ = temp;
    }

    T& GetElementAt(int nIndex) const
    {
        assert(abs(nIndex) < length_of_str_);
        if (nIndex >= 0)
            return buffer_[nIndex];
        else
            return buffer_[length_of_str_ + nIndex];
    }

    void Destory()
    {
        if (buffer_ != NULL)
        {
            delete []buffer_;
            buffer_ = NULL;
        }

        capacity_ = 0;
        length_of_str_ = 0;
    }

    void Initialize()
    {
        assert(buffer_ == NULL);
        capacity_ = 32;
        buffer_ = new T[capacity_];
        length_of_str_ = 0;
    }

private:
    T* buffer_;
    int capacity_;
    int length_of_str_;
    const static T zero;
};


typedef StringT<char> String;
template <>
const char StringT<char>::zero = '\0';

typedef StringT<wchar_t> WString;
template<>
const wchar_t StringT<wchar_t>::zero = L'\0';

JK_LIB_NAMESPACE_END

#endif //!__JK_STRING_H_
